package com.ld.common.log;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;

import com.ld.shieldsb.common.composition.util.SecurityUtil;
import com.ld.shieldsb.common.web.util.WebUtil;

import lombok.extern.log4j.Log4j2;

/**
 * log4j2 的自定义Appender，继承 AbstractAppender 只需要覆盖自已想要的方法即可<br>
 * 类上面的注解是用来设置配置文件中的标签。
 */
@Plugin(name = "DBLog4j2Appender", category = "Core", elementType = "appender", printObject = true)
@Log4j2
public class DBLog4j2Appender extends AbstractAppender {

    private static DBLogDao DB_LOG_DAO = null;
    private static String serverIp = "127.0.0.1";// 服务器公网IP
    static {
        try {
            serverIp = WebUtil.getServerIp(); // 服务器公网IP
        } catch (Exception e) {
            log.warn("获取服务器公网ip出错", e);
        }
    }

    private void init() {
        if (DB_LOG_DAO == null) {
            DB_LOG_DAO = new DBLogDao();
        }
    }

    private static String MachineName = WebUtil.getMachineName();// 服务器机器名
    private static String projectDir = WebUtil.getProjectDir();// 项目所在的tomcat目录
    private final ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private final Lock readLock = rwLock.readLock();

    // 需要实现的构造方法，直接使用父类就行
    protected DBLog4j2Appender(final String name, final Filter filter, final Layout<? extends Serializable> layout,
            final boolean ignoreExceptions) {
        super(name, filter, layout, ignoreExceptions);
    }

    @Override
    public void append(LogEvent event) {
        // 只保存抛出异常的日志且级别为error的日志
        if (event.getThrown() != null && event.getLevel().equals(Level.ERROR)) {
            try {
                // 忽略掉一切SQL异常
                if (!event.getThrown().getClass().equals(SQLException.class)) {
                    init();
                    String hash = getHash(event);
                    String throwableStrRep = event.getThrown().toString();
                    StackTraceElement[] throwable = event.getThrown().getStackTrace();
                    for (StackTraceElement ste : throwable) {
                        throwableStrRep += "\n " + ste;
                    }
//                    final byte[] bytes = getLayout().toByteArray(event);// 日志二进制文件，输出到指定位置就行
                    DB_LOG_DAO.saveOrUpdate(event.getMessage() + "", hash, throwableStrRep, serverIp, MachineName, projectDir);
                }
            } catch (Exception e) {
                // 日志保存到数据库出错，只能打印到控制台
                e.printStackTrace();
            }
            readLock.lock();
        }
    }

    // 根据message跟最后出错位置生成hash
    private String getHash(LogEvent event) {
        String hashStr = event.getThrown().toString();
        if (event.getThrown() != null) {
            StackTraceElement[] throwable = event.getThrown().getStackTrace();
            for (StackTraceElement ste : throwable) {
                hashStr += "\n " + ste;
            }
            try {
                return SecurityUtil.sha1(hashStr);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return "";
    }

    // 下面这个方法可以接收配置文件中的参数信息
    @PluginFactory
    public static DBLog4j2Appender createAppender(@PluginAttribute("name") String name, @PluginElement("Filter") final Filter filter,
            @PluginElement("Layout") Layout<? extends Serializable> layout, @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
        if (name == null) {
            LOGGER.error("No name provided for MyCustomAppenderImpl");
            return null;
        }
        if (layout == null) {
            layout = PatternLayout.createDefaultLayout();
        }
        return new DBLog4j2Appender(name, filter, layout, ignoreExceptions);
    }

    public static void main(String[] args) {
        log.warn("test");
        try {
            new Integer("7682a");

        } catch (Exception e) {
            log.error("1243", e);
        }
    }

}